package de.jmda.core.util.jpa;

import static de.jmda.core.mproc.ProcessingUtilities.getElementsAnnotatedWith;
import static de.jmda.core.mproc.ProcessingUtilities.isProcessingOver;
import static de.jmda.core.mproc.ProcessingUtilities.normalizeBinaryClassName;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.lang.annotation.Annotation;
import java.util.List;
import java.util.Set;

import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import javax.persistence.OneToOne;

import org.junit.Test;

import de.jmda.core.mproc.Processor;
import de.jmda.core.util.jpa.EntityRelation;
import de.jmda.core.util.jpa.EntityRelationInfo;
import de.jmda.core.util.jpa.EntityRelationInfoFactory;
import de.jmda.core.util.jpa.EntityRelationInfoOneToOneUnidirectional;
import de.jmda.core.util.jpa.EntityRelationInfoUnidirectional;

public class JUTEntityRelationInfoFactoryOneToOneUni extends AbstractJUTEntityRelationInfoFactory
{
	/**
	 * Will be initialised by {@link PrivateProcessor} and then analysed by {@link #test()}.
	 */
	private VariableElement field;

	/**
	 * This class will be examined by {@link PrivateProcessor}
	 */
	private class PrivateClassSource
	{
		@OneToOne
		private PrivateClassTarget target;
	}

	/**
	 * This class will be examined by {@link PrivateProcessor}
	 */
	private class PrivateClassTarget {}

	@SupportedAnnotationTypes(value = { "javax.persistence.OneToOne" })
	@SupportedSourceVersion(value = SourceVersion.RELEASE_7)
	private class PrivateProcessor extends Processor
	{
		@Override
		protected boolean process()
		{
			if (isProcessingOver() == false)
			{
				Set<VariableElement> fields =
						ElementFilter.fieldsIn(getElementsAnnotatedWith(OneToOne.class));

				for (VariableElement field : fields)
				{
					System.out.println(field.getEnclosingElement().getSimpleName() + "." + field.getSimpleName());
				}

				if (fields.size() == 1)
				{
					field = fields.iterator().next();
				}
				else
				{
					fail("unexpected size of fields: " + fields.size());
				}
			}

			if (field == null)
			{
				fail("declaredField is null");
			}

			return false;
		}

		@Override
		protected Set<Class<? extends Annotation>> getSupposedSupportedAnnotationTypes()
		{
			return asSet(OneToOne.class);
		}
	}

	@Override
	protected List<? extends Processor> getProcessors()
	{
		return asList(new PrivateProcessor());
	}

	@Test
	public void test()
	{
		EntityRelationInfo entityRelationInfo =
				EntityRelationInfoFactory.getEntityRelationInfo(field);

		assertNotNull(entityRelationInfo);

		assertTrue(entityRelationInfo instanceof EntityRelationInfoUnidirectional);
		assertTrue(entityRelationInfo instanceof EntityRelationInfoOneToOneUnidirectional);

		assertEquals(
				entityRelationInfo.getDeclaringType().toString(),
				normalizeBinaryClassName(PrivateClassSource.class));
		assertEquals(
				"target",
				entityRelationInfo.getField().getSimpleName().toString());
		assertEquals(
				field,
				entityRelationInfo.getOwningField());

		EntityRelation entityRelation = entityRelationInfo.getEntityRelation();

		assertTrue(EntityRelation.isOneToOne(entityRelation));
		assertTrue(EntityRelation.isUnidirectional(entityRelation));
	}
}